#ifdef __powerpc64__
# PowerPC64 support for -fsplit-stack.
# Copyright (C) 2009-2025 Free Software Foundation, Inc.
# Contributed by Alan Modra <amodra@gmail.com>.

# This file is part of GCC.

# GCC is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.

# GCC is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

# Under Section 7 of GPL version 3, you are granted additional
# permissions described in the GCC Runtime Library Exception, version
# 3.1, as published by the Free Software Foundation.

# You should have received a copy of the GNU General Public License and
# a copy of the GCC Runtime Library Exception along with this program;
# see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
# <http://www.gnu.org/licenses/>.

#include <auto-host.h>

#if _CALL_ELF == 2
	.abiversion 2
#define PARAMS 32
#else
#define PARAMS 48
#endif
#define MORESTACK_FRAMESIZE	(PARAMS+96)
#define R2_SAVE			-MORESTACK_FRAMESIZE+PARAMS-8
#define PARAMREG_SAVE		-MORESTACK_FRAMESIZE+PARAMS+0
#define STATIC_CHAIN_SAVE	-MORESTACK_FRAMESIZE+PARAMS+64
#define R29_SAVE		-MORESTACK_FRAMESIZE+PARAMS+72
#define LINKREG_SAVE		-MORESTACK_FRAMESIZE+PARAMS+80
#define NEWSTACKSIZE_SAVE	-MORESTACK_FRAMESIZE+PARAMS+88

# Excess space needed to call ld.so resolver for lazy plt
# resolution.  Go uses sigaltstack so this doesn't need to
# also cover signal frame size.
#define BACKOFF 4096
# Large excess allocated when calling non-split-stack code.
#define NON_SPLIT_STACK 0x100000


#if _CALL_ELF == 2

#define BODY_LABEL(name) name

#define ENTRY0(name)					\
	.global name;					\
	.hidden	name;					\
	.type name,@function;				\
name##:

#ifdef __PCREL__
#define ENTRY(name)					\
	ENTRY0(name);					\
	.localentry name, 1
#define JUMP_TARGET(name) name##@notoc
#else
#define ENTRY(name)					\
	ENTRY0(name);					\
0:	addis %r2,%r12,.TOC.-0b@ha;			\
        addi %r2,%r2,.TOC.-0b@l;			\
	.localentry name, .-name
#endif

#else

#define BODY_LABEL(name) .L.##name

#define ENTRY0(name)					\
	.global name;					\
	.hidden	name;					\
	.type name,@function;				\
	.pushsection ".opd","aw";			\
	.p2align 3;					\
name##: .quad BODY_LABEL (name), .TOC.@tocbase, 0;	\
	.popsection;					\
BODY_LABEL(name)##:

#define ENTRY(name) ENTRY0(name)

#endif

#define SIZE(name) .size name, .-BODY_LABEL(name)

#ifndef JUMP_TARGET
#define JUMP_TARGET(name) name
#endif

	.text
# Just like __morestack, but with larger excess allocation
ENTRY0(__morestack_non_split)
.LFB1:
	.cfi_startproc
# We use a cleanup to restore the tcbhead_t.__private_ss if
# an exception is thrown through this code.
#ifdef __PIC__
	.cfi_personality 0x9b,DW.ref.__gcc_personality_v0
	.cfi_lsda 0x1b,.LLSDA1
#else
	.cfi_personality 0x3,__gcc_personality_v0
	.cfi_lsda 0x3,.LLSDA1
#endif
# LR is already saved by the split-stack prologue code.
# We may as well have the unwinder skip over the call in the
# prologue too.
	.cfi_offset %lr,16

	addis %r12,%r12,-NON_SPLIT_STACK@h
	SIZE (__morestack_non_split)
# Fall through into __morestack


# This function is called with non-standard calling conventions.
# On entry, r12 is the requested stack pointer.  One version of the
# split-stack prologue that calls __morestack looks like
#	ld %r0,-0x7000-64(%r13)
#	addis %r12,%r1,-allocate@ha
#	addi %r12,%r12,-allocate@l
#	cmpld %r12,%r0
#	bge+ enough
#	mflr %r0
#	std %r0,16(%r1)
#	bl __morestack
#	ld %r0,16(%r1)
#	mtlr %r0
#	blr
# enough:
# The normal function prologue follows here, with a small addition at
# the end to set up the arg pointer.  The arg pointer is set up with:
#	addi %r12,%r1,offset
#	bge %cr7,.+8
#	mr %r12,%r29
#
# Note that the lr save slot 16(%r1) has already been used.
# r3 thru r11 possibly contain arguments and a static chain
# pointer for the function we're calling, so must be preserved.
# cr7 must also be preserved.

ENTRY0(__morestack)

#if _CALL_ELF == 2
# Functions with localentry bits of zero cannot make calls if those
# calls might change r2.  This is true generally, and also true for
# __morestack with its special calling convention.  When __morestack's
# caller is non-pcrel but libgcc is pcrel, the functions called here
# might modify r2.  r2 must be preserved on exit, and also restored
# for the call back to our caller.
	std %r2,R2_SAVE(%r1)
#endif

# Save parameter passing registers, our arguments, lr, r29
# and use r29 as a frame pointer.
	std %r3,PARAMREG_SAVE+0(%r1)
	sub %r3,%r1,%r12		# calculate requested stack size
	mflr %r12
	std %r4,PARAMREG_SAVE+8(%r1)
	std %r5,PARAMREG_SAVE+16(%r1)
	std %r6,PARAMREG_SAVE+24(%r1)
	std %r7,PARAMREG_SAVE+32(%r1)
	addi %r3,%r3,BACKOFF
	std %r8,PARAMREG_SAVE+40(%r1)
	std %r9,PARAMREG_SAVE+48(%r1)
	std %r10,PARAMREG_SAVE+56(%r1)
	std %r11,STATIC_CHAIN_SAVE(%r1)
	std %r29,R29_SAVE(%r1)
	std %r12,LINKREG_SAVE(%r1)
	std %r3,NEWSTACKSIZE_SAVE(%r1)	# new stack size
	mr %r29,%r1
#if _CALL_ELF == 2
	.cfi_offset %r2,R2_SAVE
#endif
	.cfi_offset %r29,R29_SAVE
	.cfi_def_cfa_register %r29
	stdu %r1,-MORESTACK_FRAMESIZE(%r1)

#if _CALL_ELF == 2 && !defined __PCREL__
# If this isn't a pcrel libgcc then the functions we call here will
# require r2 to be valid.  If __morestack is called from pcrel code r2
# won't be valid.  Set it up.
	bcl 20,31,1f
1:
	mflr %r12
	addis %r2,%r12,.TOC.-1b@ha
	addi %r2,%r2,.TOC.-1b@l
#endif

	# void __morestack_block_signals (void)
	bl JUMP_TARGET(__morestack_block_signals)

	# void *__generic_morestack (size_t *pframe_size,
	#			     void *old_stack,
	#			     size_t param_size)
	addi %r3,%r29,NEWSTACKSIZE_SAVE
	mr %r4,%r29
	li %r5,0			# no copying from old stack
	bl JUMP_TARGET(__generic_morestack)

# Start using new stack
	stdu %r29,-PARAMS(%r3)		# back-chain
	mr %r1,%r3

# Set __private_ss stack guard for the new stack.
	ld %r12,NEWSTACKSIZE_SAVE(%r29)	# modified size
	addi %r3,%r3,BACKOFF-PARAMS
	sub %r3,%r3,%r12
# Note that a signal frame has $pc pointing at the instruction
# where the signal occurred.  For something like a timer
# interrupt this means the instruction has already executed,
# thus the region starts at the instruction modifying
# __private_ss, not one instruction after.
.LEHB0:
	std %r3,-0x7000-64(%r13)	# tcbhead_t.__private_ss

	# void __morestack_unblock_signals (void)
	bl JUMP_TARGET(__morestack_unblock_signals)

# Set up for a call to the target function, located 3
# instructions after __morestack's return address.
#
	ld %r12,LINKREG_SAVE(%r29)
#if _CALL_ELF == 2
	ld %r2,R2_SAVE(%r29)
#endif
	ld %r3,PARAMREG_SAVE+0(%r29)	# restore arg regs
	ld %r4,PARAMREG_SAVE+8(%r29)
	ld %r5,PARAMREG_SAVE+16(%r29)
	ld %r6,PARAMREG_SAVE+24(%r29)
	ld %r7,PARAMREG_SAVE+32(%r29)
	ld %r8,PARAMREG_SAVE+40(%r29)
	ld %r9,PARAMREG_SAVE+48(%r29)
	addi %r0,%r12,12		# add 3 instructions
	ld %r10,PARAMREG_SAVE+56(%r29)
	ld %r11,STATIC_CHAIN_SAVE(%r29)
	cmpld %cr7,%r12,%r0		# indicate we were called
	mtctr %r0
	bctrl				# call caller!

# On return, save regs possibly used to return a value, and
# possibly trashed by calls to __morestack_block_signals,
# __generic_releasestack and __morestack_unblock_signals.
# Assume those calls don't use vector or floating point regs.
	std %r3,PARAMREG_SAVE+0(%r29)
	std %r4,PARAMREG_SAVE+8(%r29)
	std %r5,PARAMREG_SAVE+16(%r29)
	std %r6,PARAMREG_SAVE+24(%r29)
#if _CALL_ELF == 2
	std %r7,PARAMREG_SAVE+32(%r29)
	std %r8,PARAMREG_SAVE+40(%r29)
	std %r9,PARAMREG_SAVE+48(%r29)
	std %r10,PARAMREG_SAVE+56(%r29)
#endif

#if _CALL_ELF == 2 && !defined __PCREL__
# r2 was restored for calling back into our caller.  Set it up again.
	bcl 20,31,1f
1:
	mflr %r12
	addis %r2,%r12,.TOC.-1b@ha
	addi %r2,%r2,.TOC.-1b@l
#endif

	bl JUMP_TARGET(__morestack_block_signals)

	# void *__generic_releasestack (size_t *pavailable)
	addi %r3,%r29,NEWSTACKSIZE_SAVE
	bl JUMP_TARGET(__generic_releasestack)

# Reset __private_ss stack guard to value for old stack
	ld %r12,NEWSTACKSIZE_SAVE(%r29)
	addi %r3,%r3,BACKOFF
	sub %r3,%r3,%r12
.LEHE0:
	std %r3,-0x7000-64(%r13)	# tcbhead_t.__private_ss

	bl JUMP_TARGET(__morestack_unblock_signals)

# Use old stack again.
	mr %r1,%r29

# Restore return value regs, and return.
	ld %r0,LINKREG_SAVE(%r29)
	mtlr %r0
#if _CALL_ELF == 2
	ld %r2,R2_SAVE(%r29)
#endif
	ld %r3,PARAMREG_SAVE+0(%r29)
	ld %r4,PARAMREG_SAVE+8(%r29)
	ld %r5,PARAMREG_SAVE+16(%r29)
	ld %r6,PARAMREG_SAVE+24(%r29)
#if _CALL_ELF == 2
	ld %r7,PARAMREG_SAVE+32(%r29)
	ld %r8,PARAMREG_SAVE+40(%r29)
	ld %r9,PARAMREG_SAVE+48(%r29)
	ld %r10,PARAMREG_SAVE+56(%r29)
#endif
	ld %r29,R29_SAVE(%r29)
	.cfi_def_cfa_register %r1
	blr

# This is the cleanup code called by the stack unwinder when
# unwinding through code between .LEHB0 and .LEHE0 above.
cleanup:
	.cfi_def_cfa_register %r29
	std %r3,PARAMREG_SAVE(%r29)	# Save exception header
	# size_t __generic_findstack (void *stack)
	mr %r3,%r29
	bl JUMP_TARGET(__generic_findstack)
	sub %r3,%r29,%r3
	addi %r3,%r3,BACKOFF
	std %r3,-0x7000-64(%r13)	# tcbhead_t.__private_ss
	ld %r3,PARAMREG_SAVE(%r29)
	bl JUMP_TARGET(_Unwind_Resume)
#ifndef __PCREL__
	nop
#endif
	.cfi_endproc
	SIZE (__morestack)


	.section .gcc_except_table,"a",@progbits
	.p2align 2
.LLSDA1:
	.byte	0xff	# @LPStart format (omit)
	.byte	0xff	# @TType format (omit)
	.byte	0x1	# call-site format (uleb128)
	.uleb128 .LLSDACSE1-.LLSDACSB1	# Call-site table length
.LLSDACSB1:
	.uleb128 .LEHB0-.LFB1	# region 0 start
	.uleb128 .LEHE0-.LEHB0	# length
	.uleb128 cleanup-.LFB1	# landing pad
	.uleb128 0		# no action, ie. a cleanup
.LLSDACSE1:


#ifdef __PIC__
# Build a position independent reference to the personality function.
	.hidden DW.ref.__gcc_personality_v0
	.weak DW.ref.__gcc_personality_v0
	.section .data.DW.ref.__gcc_personality_v0,"awG",@progbits,DW.ref.__gcc_personality_v0,comdat
	.p2align 3
DW.ref.__gcc_personality_v0:
	.quad __gcc_personality_v0
	.type DW.ref.__gcc_personality_v0, @object
	.size DW.ref.__gcc_personality_v0, 8
#endif


	.text
# Initialize the stack guard when the program starts or when a
# new thread starts.  This is called from a constructor.
# void __stack_split_initialize (void)
ENTRY(__stack_split_initialize)
	.cfi_startproc
	addi %r3,%r1,-0x4000		# We should have at least 16K.
	std %r3,-0x7000-64(%r13)	# tcbhead_t.__private_ss
	# void __generic_morestack_set_initial_sp (void *sp, size_t len)
	mr %r3,%r1
	li %r4, 0x4000
	b JUMP_TARGET(__generic_morestack_set_initial_sp)
# The lack of .cfi_endproc here is deliberate.  This function and the
# following ones can all use the default FDE.
	SIZE (__stack_split_initialize)


# Return current __private_ss
# void *__morestack_get_guard (void)
ENTRY0(__morestack_get_guard)
	ld %r3,-0x7000-64(%r13)		# tcbhead_t.__private_ss
	blr
	SIZE (__morestack_get_guard)


# Set __private_ss
# void __morestack_set_guard (void *ptr)
ENTRY0(__morestack_set_guard)
	std %r3,-0x7000-64(%r13)	# tcbhead_t.__private_ss
	blr
	SIZE (__morestack_set_guard)


# Return the stack guard value for given stack
# void *__morestack_make_guard (void *stack, size_t size)
ENTRY0(__morestack_make_guard)
	sub %r3,%r3,%r4
	addi %r3,%r3,BACKOFF
	blr
	.cfi_endproc
	SIZE (__morestack_make_guard)


# Make __stack_split_initialize a high priority constructor.
#if HAVE_INITFINI_ARRAY_SUPPORT
	.section .init_array.00000,"aw",@init_array
#else
	.section .ctors.65535,"aw",@progbits
#endif
	.p2align 3
	.quad __stack_split_initialize
	.quad __morestack_load_mmap

	.section .note.GNU-stack,"",@progbits
	.section .note.GNU-split-stack,"",@progbits
	.section .note.GNU-no-split-stack,"",@progbits
#endif /* __powerpc64__ */
